// 版权所有2022 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package types

// validType验证给定类型是否不会无限期“扩展”
// 在类型图中生成一个循环。通过标记
// 定义的类型来检测循环。
// （涉及别名类型的循环，如“type A=[10]A”中的循环，通过objDecl循环检测机制检测到
// 。）
func (check *Checker) validType(typ *Named) {
	check.validType0(typ, nil, nil)
}

type typeInfo uint

// validType0检查给定类型是否有效。如果typ是一个类型参数
// 则在提供的环境中查找其值。如果typ不是实例化类型（RHS）的一部分，则环境为
// nil，在这种情况下，
// 遇到的任何类型参数都必须来自封闭函数，并且可以忽略
// 。路径是指向当前类型的类型名列表。
func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeInfo {
	const (
		unknown typeInfo = iota
		marked
		valid
		invalid
	)

	switch t := typ.(type) {
	case nil:
		// 我们不应该看到nil类型，而应该保守和恐慌
		// 只在调试模式下。
		if debug {
			panic("validType0(nil)")
		}

	case *Array:
		return check.validType0(t.elem, env, path)

	case *Struct:
		for _, f := range t.fields {
			if check.validType0(f.typ, env, path) == invalid {
				return invalid
			}
		}

	case *Union:
		for _, t := range t.terms {
			if check.validType0(t.typ, env, path) == invalid {
				return invalid
			}
		}

	case *Interface:
		for _, etyp := range t.embeddeds {
			if check.validType0(etyp, env, path) == invalid {
				return invalid
			}
		}

	case *Named:
		// 如果我们已经知道类型无效，请不要报告第二个错误
		// （例如，如果之前检测到一个循环，请通过under）。
		if t.underlying == Typ[Invalid] {
			check.infoMap[t] = invalid
			return invalid
		}

		switch check.infoMap[t] {
		case unknown:
			check.infoMap[t] = marked
			check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj))
		case marked:
			// 我们以前见过t型，因此一定有一个循环。
			check.infoMap[t] = invalid
			// t不能在导入的包中，否则该包
			// 将报告一个类型循环，并且不能首先导入
			// 。
			assert(t.obj.pkg == check.pkg)
			t.underlying = Typ[Invalid] // t在当前包中（不存在竞争可能性）
			// 找到循环的起点并报告。
			for i, tn := range path {
				if tn == t.obj {
					check.cycleError(path[i:])
					return invalid
				}
			}
			panic("cycle start not found")
		}
		return check.infoMap[t]

	case *TypeParam:
		// 类型参数代表它被实例化的类型（参数）。
		// 如果我们有相应的类型参数，请检查其有效性。
		if env != nil {
			if targ := env.tmap[t]; targ != nil {
				// 在targ中找到的类型参数必须在封闭的环境env中查找
				// 向上。链接
				return check.validType0(targ, env.link, path)
			}
		}
	}

	return valid
}

// tparamEnv提供了查找类型参数的环境
// 使用这些参数实例化了给定实例的类型参数。
// 如果没有实例，对应的tparamEnv为零。
type tparamEnv struct {
	tmap substMap
	link *tparamEnv
}

func (env *tparamEnv) push(typ *Named) *tparamEnv {
	// 如果typ不是实例化的类型，则没有特定于类型的
	// 要查找的类型参数，我们不需要环境。
	targs := typ.TypeArgs()
	if targs == nil {
		return nil // 无实例=>nil环境
	}

	// 填充tmap：记住每个类型参数的类型参数。
	// 我们无法使用MakeSubsMap，因为类型参数
	// 和参数的数量可能由于源中的错误（太多
	// 或太少类型参数）而不匹配。“手动”填充tmap。
	tparams := typ.TypeParams()
	n, m := targs.Len(), tparams.Len()
	if n > m {
		n = m // 太多目标
	}
	tmap := make(substMap, n)
	for i := 0; i < n; i++ {
		tmap[tparams.At(i)] = targs.At(i)
	}

	return &tparamEnv{tmap: tmap, link: env}
}

// TODO（gri）替代实现：
// 我们可能不需要构建一个环境堆栈来
// 查找类型参数的类型参数。
// 同样的信息应该可以通过路径获得：
// 我们应该能够向后走
// 并在实例对象中找到类型参数。
